package com.liziba.trend.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.liziba.trend.api.IndexData;
import com.liziba.trend.api.Profit;
import com.liziba.trend.api.Trade;
import com.liziba.trend.service.BackTestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * @program: trend
 * @description:
 * @author: liziba
 * @date: 2021-02-03 12:19
 */
@RestController
@CrossOrigin
public class BackTestController {

    /**
     * 获取指定基金代码数据服务
     */
    @Autowired
    private BackTestService backTestService;

    /**
     *
     * 模拟回测
     * @param code                  基金代码
     * @param ma                    MA均线 MA 即 moving average, 移动均线的意思。 比如MA20就表示20日均线，取最近20天的值的平均数。
     * @param buyThreshold          购买阈值    如果当前的收盘点高于这个均线一定的比例，那么我们认为上涨的趋势可能就来了，就可以买了。
     * @param sellThreshold         卖出阈值    如果当前的收盘点，比起最近的20个交易日里的最高的点，跌了 5%或者 10%了，那么我们认为下跌趋势可能就来了，就可以卖了。
     * @param serviceCharge         手续费
     * @param startDate             开始日期
     * @param endDate               结束日期
     * @return
     */
    @GetMapping("/simulate/{code}/{ma}/{buyThreshold}/{sellThreshold}/{serviceCharge}/{startDate}/{endDate}")
    public Map<String,Object> backTest(@PathVariable(value = "code") String code,
                                       @PathVariable(value = "ma") int ma,
                                       @PathVariable(value = "buyThreshold") float buyThreshold,
                                       @PathVariable(value = "sellThreshold") float sellThreshold,
                                       @PathVariable(value = "serviceCharge") float serviceCharge,
                                       @PathVariable(value = "startDate") String startDate,
                                       @PathVariable(value = "endDate") String endDate) {
        List<IndexData> indexData = backTestService.getIndexData(code);
        List<IndexData> indexDataList = filterByDateRange(indexData, startDate, endDate);
        // 计算前端需要数据
        Map<String, Object> simulate = backTestService.simulate(ma, sellThreshold, buyThreshold, serviceCharge, indexDataList);
        List<Profit> profits = (List<Profit>)simulate.get("profits");
        List<Trade> trades = (List<Trade>)simulate.get("trades");
        // 年份
        float years = backTestService.getYears(indexDataList);
        // 普通投资总收益
        float indexIncomeTotal = (indexDataList.get(indexDataList.size() - 1).getClosePoint() - indexDataList.get(0).getClosePoint()) / indexDataList.get(0).getClosePoint() ;
        // 年化收益率=[（投资内收益 / 本金）/ 投资天数] *365 ×100%
        float indexIncomeAnnual = (float) Math.pow(1+indexIncomeTotal, 1/years) -1;
        // 趋势投资总收益
        float trendIncomeTotal = (profits.get(indexDataList.size() - 1).getValue() - profits.get(0).getValue()) / profits.get(0).getValue();
        // 趋势投资年化收益
        float trendIncomeAnnual = (float) Math.pow(1+trendIncomeTotal, 1/years) -1;
        // 盈利次数
        int winCount = (Integer) simulate.get("winCount");
        // 亏损次数
        int lossCount = (Integer) simulate.get("lossCount");
        // 平均盈利率
        float avgWinRate = (Float) simulate.get("avgWinRate");
        // 平均亏损率
        float avgLossRate = (Float) simulate.get("avgLossRate");

        Map<String,Object> result = new HashMap<>();
        result.put("indexDatas", indexDataList);
        result.put("indexStartDate", indexData.get(0).getDate());
        result.put("indexEndDate", indexData.get(indexData.size()-1).getDate());
        // 趋势投资
        result.put("profits", profits);
        // 交易明细
        result.put("trades", trades);
        // 指数投资、趋势投资总利率&年利率
        result.put("years", years);
        result.put("indexIncomeTotal", indexIncomeTotal);
        result.put("indexIncomeAnnual", indexIncomeAnnual);
        result.put("trendIncomeTotal", trendIncomeTotal);
        result.put("trendIncomeAnnual", trendIncomeAnnual);
        // 趋势投资盈、亏比率&次数
        result.put("winCount", winCount);
        result.put("lossCount", lossCount);
        result.put("avgWinRate", avgWinRate);
        result.put("avgLossRate", avgLossRate);
        // 计算每一年指数投资和趋势投资的收益率
        result.put("annualProfits", simulate.get("annualProfits"));
        return result;
    }

    /**
     * 根据日期过滤基金数据
     * @param indexData
     * @param strStartDate
     * @param strEndDate
     * @return
     */
    private List<IndexData> filterByDateRange(List<IndexData> indexData, String strStartDate, String strEndDate) {
        if(StrUtil.isBlankOrUndefined(strStartDate) || StrUtil.isBlankOrUndefined(strEndDate)) {
            return indexData;
        }
        // 开始时间
        Date startDate = DateUtil.parse(strStartDate);
        // 结束时间
        Date endDate = DateUtil.parse(strEndDate);
        // 结果集
        List<IndexData> result = new ArrayList<>(getListSizeByDateRange(startDate, endDate));
        for (IndexData indexDatum : indexData) {
            Date indexDate = DateUtil.parse(indexDatum.getDate());
            if(indexDate.getTime() <= endDate.getTime() && indexDate.getTime() >= startDate.getTime()) {
                result.add(indexDatum);
            }
        }
        return result;
    }

    /**
     * 根据时间计算集合大小   ---- 一个星期算5天
     * @param startDate     开始时间
     * @param endDate       结束时间
     * @return
     */
    private int getListSizeByDateRange(Date startDate, Date endDate) {
        return  (int)((((endDate.getTime() - startDate.getTime()) / (60 * 60 * 24)) /  7)  * 5);
    }


}
